package com.yungu.swift.utils;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * HTTP请求工具类
 *
 * @author cuixiuyin
 * @date 2018/10/24
 */
public class HttpUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    private static final String HTTPS = "https";
    private static final String GOOGLE_API = "googleapis";
    public static final int RESULT_TYPE_STRING = 1;
    public static final int RESULT_TYPE_BYTE_ARRAY = 2;

    public static final int ENTITY_TYPE_FORM = 1;
    public static final int ENTITY_TYPE_MULTIPART = 2;
    public static final int ENTITY_TYPE_JSON = 3;

    private static final String PROXY_URL = "127.0.0.1";
    private static final int PROXY_PORT = 18888;

    private HttpUtils() {
    }

    public static Map<String, Object> get(String url) {
        return get(url, RESULT_TYPE_STRING, new HashMap<>(0));
    }

    public static Map<String, Object> get(String url, Map<String, String> headerMap) {
        return get(url, RESULT_TYPE_STRING, headerMap);
    }

    public static Map<String, Object> get(String url, int resultType, Map<String, String> headerMap) {
        Map<String, Object> resultMap = new HashMap<>(256);
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = createDefaultClient(url);
            HttpGet httpGet = new HttpGet(url);
            //连接设置
            httpGet.setConfig(config());
            //浏览器模式
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
            //自定义头部
            if (!headerMap.isEmpty()) {
                for (String key : headerMap.keySet()) {
                    httpGet.setHeader(key, headerMap.get(key));
                }
            }
            logger.info("send HTTP GET request to [" + url + "]");
            response = client.execute(httpGet);
            //处理返回结果
            resultMap.put("status", response.getStatusLine().getStatusCode());
            HttpEntity httpEntity = response.getEntity();
            if (resultType == RESULT_TYPE_STRING) {
                resultMap.put("result", EntityUtils.toString(httpEntity, StandardCharsets.UTF_8));
            }
            if (resultType == RESULT_TYPE_BYTE_ARRAY) {
                resultMap.put("result", EntityUtils.toByteArray(httpEntity));
            }
            //消费entity
            EntityUtils.consume(httpEntity);
        } catch (IOException e) {
            logger.error("", e);
            resultMap.put("status", -1);
            resultMap.put("result", e.getMessage());
        } finally {
            close(client, response);
        }
        return resultMap;
    }

    public static Map<String, Object> post(String url, Map<String, Object> map) {
        return post(url, ENTITY_TYPE_FORM, map, new Properties(), new HashMap<>(0));
    }

    public static Map<String, Object> post(String url, Map<String, Object> map, Map<String, String> headerMap) {
        return post(url, ENTITY_TYPE_FORM, map, new Properties(), headerMap);
    }

    public static Map<String, Object> post(String url, Map<String, Object> map, Properties properties) {
        return post(url, ENTITY_TYPE_FORM, map, properties, new HashMap<>(0));
    }

    public static Map<String, Object> post(String url, int entityType, Map<String, Object> map, Map<String, String> headerMap) {
        return post(url, entityType, map, new Properties(), headerMap);
    }

    public static Map<String, Object> post(String url, int entityType, Map<String, Object> map, Properties properties, Map<String, String> headerMap) {
        return post(url, entityType, map, properties, headerMap, false);
    }

    public static Map<String, Object> post(String url, int entityType, Map<String, Object> map, Properties properties, Map<String, String> headerMap, boolean needProxy) {
        Map<String, Object> resultMap = new HashMap<>(256);
        CloseableHttpClient client = null;
        CloseableHttpResponse response = null;
        try {
            client = createDefaultClient(url);
            HttpPost httpPost = new HttpPost(url);
            //-----连接配置
            httpPost.setConfig(config());
            //-----设置内容
            //表单型请求
            if (entityType == ENTITY_TYPE_FORM) {
                httpPost.setEntity(new UrlEncodedFormEntity(buildFormParams(map, properties), StandardCharsets.UTF_8));
            }
            //复杂型请求(文件)
            if (entityType == ENTITY_TYPE_MULTIPART) {
                MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
                multipartEntityBuilder.setCharset(StandardCharsets.UTF_8);
                buildMultipartParams(multipartEntityBuilder, map, properties);
                httpPost.setEntity(multipartEntityBuilder.build());
            }
            //字符串型请求(JSON)
            if (entityType == ENTITY_TYPE_JSON) {
                headerMap.put("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
                httpPost.setEntity(new StringEntity((String) map.get("_JSON_STRING"), ContentType.APPLICATION_JSON.getCharset()));
            }
            //-----设置头部
            //浏览器模式
            httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
            //自定义头部
            if (!headerMap.isEmpty()) {
                for (String key : headerMap.keySet()) {
                    httpPost.setHeader(key, headerMap.get(key));
                }
            }
            logger.info("send HTTP POST request to [" + url + "]");
            response = client.execute(httpPost);
            // status
            resultMap.put("status", response.getStatusLine().getStatusCode());
            HttpEntity httpEntity = response.getEntity();
            // do something useful with the response body
            resultMap.put("result", EntityUtils.toString(httpEntity));
            // and ensure it is fully consumed
            EntityUtils.consume(httpEntity);
        } catch (IOException e) {
            logger.error("", e);
        } finally {
            close(client, response);
        }
        return resultMap;
    }

    public static String getResponseString(String url) {
        return responseString(get(url));
    }

    public static String postResponseString(String url, Map<String, Object> map) {
        return responseString(post(url, map));
    }

    private static String responseString(Map<String, Object> resultMap) {
        return (String) resultMap.get("result");
    }

    private static RequestConfig config() {
        return RequestConfig.copy(RequestConfig.DEFAULT)
                .setConnectionRequestTimeout(120000).setConnectTimeout(60000).setSocketTimeout(60000).build();
    }

    public static void close(CloseableHttpClient client, CloseableHttpResponse response) {
        try {
            if (client != null) {
                client.close();
            }
            if (response != null) {
                response.close();
            }
        } catch (IOException e) {
            logger.error("HttpUtils close error.", e);
        }
    }

    public static CloseableHttpClient createDefaultClient(String url) {
        HttpClientBuilder builder = HttpClients.custom();
        CloseableHttpClient client;
        boolean enableProxy = checkProxyEnable();
        boolean isHttps = url.contains(HTTPS);
        if (enableProxy || isHttps) {
            if (url.contains(GOOGLE_API) && enableProxy) {
                HttpHost proxy = new HttpHost(PROXY_URL, PROXY_PORT);
                builder.setProxy(proxy);
            }
            if (isHttps) {
                try {
                    SSLContext sslContext = (new SSLContextBuilder()).loadTrustMaterial(null, (TrustStrategy) (x509Certificates, s) -> {
                        //信任所有
                        return true;
                    }).build();
                    SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext);
                    builder.setSSLSocketFactory(sslConnectionSocketFactory);
                } catch (GeneralSecurityException var3) {
                    var3.printStackTrace();
                }
            }
            client = builder.build();
        } else {
            client = HttpClients.custom().setMaxConnTotal(300).setMaxConnPerRoute(30)
                    .evictExpiredConnections().evictIdleConnections(30L, TimeUnit.SECONDS)
                    .build();
        }
        return client;
    }

    private static List<NameValuePair> buildFormParams(Map<String, Object> map, Properties properties) {
        Enumeration<?> e = properties.propertyNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            if (!map.containsKey(key) || map.get(key) == null || StringUtils.isEmpty(map.get(key).toString())) {
                map.put(key, properties.getProperty(key));
            }
        }
        List<NameValuePair> params = new ArrayList<>();
        for (String key : map.keySet()) {
            Object value = map.get(key);
            //空值忽略
            if (value == null) {
                continue;
            }
            String valueStr = value.toString();
            if (value instanceof Timestamp) {
                valueStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
            }
            params.add(new BasicNameValuePair(key, valueStr));
        }
        return params;
    }

    private static void buildMultipartParams(MultipartEntityBuilder multipartEntityBuilder, Map<String, Object> map,
                                             Properties properties) {
        Enumeration<?> e = properties.propertyNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            if (map.containsKey(key)) {
                map.remove(key);
            }
            map.put(key, properties.getProperty(key));
        }
        ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
        for (String key : map.keySet()) {
            Object value = map.get(key);
            if (value == null) {
                continue;
            }
            //转换
            String valueStr = value.toString();
            if (value instanceof Integer) {
                valueStr = String.valueOf(value);
            }
            if (value instanceof Timestamp) {
                valueStr = new SimpleDateFormat("yyyyMMddHHmmss").format(value);
            }
            multipartEntityBuilder.addPart(key, new StringBody(valueStr, contentType));
        }
    }

    private static boolean checkProxyEnable() {
        String enableProxy = YamlUtils.getFromDefault("enableProxy");
        return StringUtils.isNotEmpty(enableProxy) && Boolean.parseBoolean(enableProxy);
    }

}
